#include "SQLSelectStatement.h"
#include "../../visitor/SQLASTVisitor.h"
#include "SQLSelectQueryBlock.h"

SQLSelectStatement::SQLSelectStatement()
{
}

SQLSelectStatement::SQLSelectStatement(DbType_ptr dbType)
    : SQLStatementImpl(dbType)
{
}

SQLSelectStatement::SQLSelectStatement(SQLSelect_ptr select)
{
  this->setSelect(select);
}

SQLSelectStatement::SQLSelectStatement(SQLSelect_ptr select, DbType_ptr dbType)
    : SQLSelectStatement(dbType)
{
  this->setSelect(select);
}

SQLSelect_ptr SQLSelectStatement::getSelect()
{
  return this->select;
}

void SQLSelectStatement::setSelect(SQLSelect_ptr select)
{
  if (select != nullptr)
  {
    select->setParent(SharedObject(SQLSelectStatement));
  }
  this->select = select;
}

void SQLSelectStatement::output(std::stringstream * buf)
{
  this->select->output(buf);
}

void SQLSelectStatement::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLSelectStatement)))
  {
    if (this->select != nullptr)
    {
      this->select->accept(visitor);
    }
  }
  visitor->endVisit(SharedObject(SQLSelectStatement));
}

SQLObject_ptr SQLSelectStatement::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<SQLSelectStatement>(new SQLSelectStatement());
  std::shared_ptr<SQLSelectStatement> x = std::dynamic_pointer_cast<SQLSelectStatement>(clone_tmp);
  // SQLSelectStatement *x = new SQLSelectStatement();
  x->dbType = dbType;
  x->afterSemi = afterSemi;
  if (select != nullptr)
  {
    x->setSelect(std::dynamic_pointer_cast<SQLSelect>(select->clone()));
  }
  if (!headHints->empty())
  {
    for (SQLCommentHint_ptr h : *headHints)
    {
      SQLCommentHint_ptr h2 = std::dynamic_pointer_cast<SQLCommentHint>(h->clone());
      h2->SQLObjectImpl::setParent(x);
      // if (x->headHints == nullptr)
      // {
      //   x->headHints = new ArrayList<SQLCommentHint *>(headHints.size());
      // }
      x->headHints->push_back(h2);
    }
  }
  return x;
}

SQLObject_list_ptr SQLSelectStatement::getChildren()
{
  SQLObject_list_ptr tmp = std::make_shared<std::list<SQLObject_ptr>>();
  tmp->push_back(std::shared_ptr<SQLSelect>(select));
  return tmp;
}

bool SQLSelectStatement::equals(Object_ptr o_)
{
  SQLSelectStatement_ptr o = std::dynamic_pointer_cast<SQLSelectStatement>(o_);
  if (this == o.get())
  {
    return true;
  }
  if (o == nullptr || getClass() != o->getClass())
  {
    return false;
  }

  SQLSelectStatement_ptr that = o;

  return select != nullptr ? select->equals(that->select) : that->select == nullptr;
}

int SQLSelectStatement::hashCode()
{
  return select != nullptr ? select->hashCode() : 0;
}

string_list_ptr SQLSelectStatement::computeSelecteListAlias()
{
  return select->computeSelecteListAlias();
}

BOOL_ptr SQLSelectStatement::addWhere(SQLExpr_ptr where)
{
  return select->addWhere(where);
}

void SQLSelectStatement::addBeforeComment(string_ptr comment)
{
  if (headHints->empty() &&
      attributes->empty() &&
      select->hints->empty() &&
      select->getBeforeCommentsDirect()->empty())
  {
    SQLSelectQueryBlock_ptr queryBlock = select->getQueryBlock();
    string_list_ptr queryBlockBeforeComments = queryBlock->getBeforeCommentsDirect();
    if (!queryBlockBeforeComments->empty())
    {
      queryBlockBeforeComments->push_back((comment));
      return;
    }
  }
  this->addBeforeComment(comment);
}
